#include <ctime>
#include <iostream>
#include <string>
#include <memory>
#include <asio.hpp>

using asio::ip::tcp;

std::string make_daytime_string()
{
    using namespace std;
    time_t now = time(0);
    return ctime(&now);
}

struct Connection {
    using SharedPtr = std::shared_ptr<Connection>;
    using UniquePtr = std::unique_ptr<Connection>;
    
    tcp::socket socket_;
    std::string message_{};

    Connection(asio::io_context& io) : socket_(io) {}
};

struct OnWriteCompletion
{
    Connection::UniquePtr connection_;

    void operator() (const std::error_code&, size_t) {
    }
};

struct OnAcceptCompletion
{
    asio::io_context& io_;
    tcp::acceptor& acceptor_;
    Connection::UniquePtr connection_;

    void operator() (const std::error_code& ec) {
        if (!ec) {
            OnWriteCompletion on_write{std::move(connection_)};
            on_write.connection_->message_ = std::string(make_daytime_string());
            asio::async_write(on_write.connection_->socket_, asio::buffer(on_write.connection_->message_), std::move(on_write));
        }

        OnAcceptCompletion on_next_accept{io_, acceptor_, std::make_unique<Connection>(io_)};
        acceptor_.async_accept(on_next_accept.connection_->socket_, std::move(on_next_accept));
    }
};

int main (int argc, char *argv[]) {
    try {
        asio::io_context io;
        tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 8888));

        OnAcceptCompletion on_accept{io, acceptor, std::make_unique<Connection>(io)};
        acceptor.async_accept(on_accept.connection_->socket_, std::move(on_accept));

        io.run();
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}
